/**
 * Copyright (c) 2009 blurredVision
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
 * associated documentation files (the "Software"), to deal in the Software without restriction, 
 * including without limitation the rights to use, copy, modify, merge, publish, distribute, 
 * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or 
 * substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 
 * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT 
 * OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
#include "StdAfx.h"
#include "ICamera.h"
#include "core/core.h"
#ifdef CORE_DIRECTX
	#include "dx/Camera.h"
#endif

ICamera::ICamera(void)
 :	vPosition(math::Vector3(0,0,10)),
	vTarget(math::Vector3(0,0,0)),
	vUpVec(math::Vector3(0,1,0)),
	hRadians(0),
	vRadians(0),
	m_bOrtho(false)
{
}

ICamera::~ICamera(void)
{
}

ICamera* ICamera::Create()
{
	ICamera* resource = 0;

#ifdef CORE_DIRECTX
	resource = new directx::Camera();
#endif
	return resource;
}

void ICamera::setPosition(const math::Vector3 &vector)
{
	vPosition = vector;
	
	float radius = (vPosition - vTarget).length();
	vRadians = asinf(vPosition.y / radius);
	hRadians = -acosf( vPosition.x / (cosf(vRadians) * radius));
	
	updateMatrix();
}

void ICamera::setTarget(const math::Vector3 &vector)
{
	vTarget = vector;
	updateMatrix();
}

void ICamera::setUpVec(const math::Vector3 &vector)
{
	vUpVec = vector;
	updateMatrix();
}

const math::Vector3& ICamera::getPosition() const
{
	return vPosition;
}

const math::Vector3& ICamera::getTarget() const
{
	return vTarget;
}

const math::Vector3& ICamera::getUpVec() const
{
	return vUpVec;
}

void ICamera::resetAll()
{
	hRadians  = 0;
	vRadians  = 0;
	vPosition = math::Vector3(0,0,0);
	vUpVec    = math::Vector3(0,0,0);
	vTarget   = math::Vector3(0,0,0);
}

void ICamera::moveAlongViewVec(float z) 
{
	math::Vector3 vDir = vTarget - vPosition;
	vDir.normalize();
	vPosition += vDir * z;

	updateMatrix();
}

void ICamera::rotateAroundTarget(float h, float v) 
{
    //STDCOUT(hRadians);
	//STDCOUT(vRadians);

	
	hRadians -= h;
    vRadians += v;
	
	// Restriction of vertical-movement (By Dennis)
	if (vRadians > 1.55f) {
		vRadians = 1.55f;
	}
	if (vRadians < -0.05f) {
		vRadians = -0.05f;
	}

	float radius = (vPosition - vTarget).length();

	vPosition.x = (float)(radius*cosf(vRadians)*cosf(hRadians));
	vPosition.y = (float)(radius*sinf(vRadians));
    vPosition.z = (float)(radius*cosf(vRadians)*sinf(hRadians));
    
	vPosition += vTarget;

	updateMatrix();
}


void ICamera::set_hRadians(float h) 
{
	hRadians = h;
    	
	// Restriction of verkical-movement (By Dennis)
	if (vRadians > 1.25) {
		vRadians = 1.25f;
	}
	if (vRadians < -0.25) {
		vRadians = -0.25f;
	}

	float radius = (vPosition - vTarget).length();

	vPosition.y = (float)(radius*sinf(vRadians));
	vPosition.x = (float)(radius*cosf(vRadians)*cosf(hRadians));
    vPosition.z = (float)(radius*cosf(vRadians)*sinf(hRadians));
    
	updateMatrix();
}

void ICamera::rotate(const math::Quaternion &q)
{
	math::Vector3 dir = vTarget - vPosition;
	//math::Vector3 newDir = q * dir * -q;
	
	math::Quaternion q2 = q;	
	math::Vector3 newDir = q2.rotate(dir);
	
	vTarget = newDir + vPosition;
	updateMatrix();
}

math::Matrix4 ICamera::getViewMatrix()
{
	return matView;
}

math::Matrix4 ICamera::getProjMatrix()
{
	return matProj;
}

void ICamera::setOrtho(bool on)
{
	m_bOrtho = on;
	updateMatrix();
}

float* ICamera::calculateConvexHull( )
{
	float *pFrust = getFrustum();
/*
[0][0] = rechts.x
[0][0] = rechts.x
[0][0] = rechts.x
[0][0] = rechts.x
*/
	return getFrustum();
}

/*
void ICamera::setClippingPlanes( float zNear, float zFar)
{
	if (m_bOrtho) 
	{
			D3DXMatrixOrthoLH( (D3DXMATRIX*) &matProj[0], (float)coreGetRenderer()->getBackBufferSize().x, (float)coreGetRenderer()->getBackBufferSize().y, zNear, zFar );
	} else 
	{
			D3DXMatrixPerspectiveFovLH( (D3DXMATRIX*) &matProj[0], fFov, fAspect, zNear, zFar );
	}
}
*/